﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Nature
{
    [AttributeUsage(AttributeTargets.Property, AllowMultiple=false)]
    public sealed class MixturePropertyAttribute : System.Attribute
    {
        
    }
    

    struct ValueHolder
    {
        private double m_value;

        internal bool UpToDate { get; private set; }

        internal double Value 
        {
            get 
            {
                if (!UpToDate)
                {
                    Debug.Assert(Update != null);
                    m_value = Update();
                    UpToDate = true;
                }
                return m_value;
            }
            set 
            {
                UpToDate = true;
                m_value = value;
            }
        }

        internal void DropState() { UpToDate = false; }

        internal Func<double> Update { get; set; }
    }

    [Serializable]
    struct DoubleArrayHolder
    {
        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        double[] m_array;


        internal bool UpToDate
        {
            get;
            private set;
        }

        public double[] Array
        {
            get 
            {
                if (!UpToDate)
                {
                    if (m_array == null) { m_array = new double[Size]; }
                    Debug.Assert(Update != null);
                    Update(m_array);
                    UpToDate = true;
                }
                return m_array;
            }
        }

        internal int Size
        {
            get;
            private set;
        }

        internal DoubleArrayHolder(int length)
            : this()
        {
            this.Size = length;
        }

        internal Action<double[]> Update { get; set; }

        internal void DropState()
        {
            UpToDate = false;
        }

        internal void Assign(double[] values)
        {
            if (m_array == null) { m_array = new double[Size]; }
            for (int i = 0; i < this.Size; ++i)
            {
                m_array[i] = values[i];
            }
            UpToDate = true;
        }


        internal void Assign(double[] values, Func<int, int> indexMap, double defaultValue)
        {
            if (m_array == null) { m_array = new double[Size]; }
            for (int i = 0; i < Size; ++i) { m_array[i] = defaultValue; }
            for (int i = 0; i < values.Length; ++i)
            {
                int index = indexMap(i);
                m_array[index] = values[i];
            }
            UpToDate = true;
        }

    }


    [Serializable]
    public struct DoubleMatrixHolder
    {
        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        double[][] m_matrix;
        Func<int, int> m_getDimension;

        internal DoubleMatrixHolder(int iLength, Func<int, int> getDimension) : this()
        {
            this.ILength = iLength;
            this.m_getDimension = getDimension;
        }

        internal bool UpToDate
        {
            get;
            private set;
        }

        public double[][] Matrix
        {
            get
            {
                if (!UpToDate)
                {
                    if (m_matrix == null) 
                    { 
                        m_matrix = new double[ILength][];
                        for (int i = 0; i < ILength; ++i)
                        {
                            int jLength = m_getDimension(i);
                            m_matrix[i] = new double[jLength];
                        }
                        m_getDimension = null;
                    }                    
                    Debug.Assert(Update != null);
                    Update(m_matrix);
                    UpToDate = true;
                }
                return m_matrix;
            }
        }

        internal int ILength
        {
            get;
            private set;
        }

        internal void DropState()
        {
            this.UpToDate = false;
        }

        internal Action<double[][]> Update { get; set; }
    }
    
}
